/*
 * @Author: liuzelin
 * @Date: 2024-04-20 12:03:05
 * @LastEditors: liuzelin
 * @LastEditTime: 2024-05-12 21:27:34
 * @Description: file content
 */
#include "panda_socket_impl.hpp"

#include "panda_log.hpp"
#include "panda_thread_impl.hpp"
#include "panda_uv_helper.hpp"
#include <cstring>

using namespace PandaImpl;

namespace {
thread_local sockaddr uvAddress;
constexpr int maxStreamNum = 100;
const sockaddr *GetIp4UVAddress(const Panda::SocketAddress &address) {
  int ret =
      uv_ip4_addr(address.Ip(), address.Port(), (sockaddr_in *)&uvAddress);
  PandaCheck(ret == 0) << " GetUVAddress:" << ret
                       << " Error:" << uv_err_name(ret);
  return &uvAddress;
}
}

Panda::SocketAddress::SocketAddress(const char *ip, uint16_t port) {
  this->ip = ip;
  this->port = port;
  addr_str_ = ip;
  addr_str_ += ":";
  addr_str_ += std::to_string(port);
}

Panda::SocketAddress::SocketAddress(const char *addr) {
  const char *pos = strchr(addr, ':');
  // PandaCheck(pos != nullptr) << " Can`t Find : From Addr:" << addr;
  this->ip = std::string(addr, pos - addr);
  this->port = std::atoi(pos + 1);
  addr_str_ = addr;
}

UDPSocketImpl::UDPSocketImpl(ThreadImpl *thread) {
  this->thread_ = thread;
  this->handler = &socket_;
  PandaCheck(ThreadManagerImpl::Instance()->Current() == thread_);
  uv_handle_set_data((uv_handle_t *)&this->socket_, this);
  int ret = uv_udp_init_ex((uv_loop_t *)this->thread_->GetLoop(),
                           &this->socket_, AF_INET);
  PandaCheck(ret == 0) << " Ret:" << ret << " Error:" << uv_err_name(ret);
  this->isClosed = false;
}
UDPSocketImpl::~UDPSocketImpl() {
  // PandaCheck(ThreadManagerImpl::Instance()->Current() == thread_);
  this->thread_ = nullptr;
  if (this->buffer_) {
    free(this->buffer_);
    this->buffer_ = nullptr;
  }
}

void UDPSocketImpl::UpdateLocalAddress() {
  int addrLen = sizeof(sockaddr);
  struct sockaddr_in uvAddress;
  int ret = uv_udp_getsockname((uv_udp_t *)&this->socket_,
                               (sockaddr *)&uvAddress, &addrLen);
  PandaCheck(ret == 0) << " Get bind address Ret:" << ret
                       << " Error:" << uv_err_name(ret);
  char ip[128] = {0};
  uv_ip_name((const sockaddr *)&uvAddress, ip, 128);
  this->local_ = Panda::SocketAddress(ip, ntohs(uvAddress.sin_port));
  ret = uv_udp_recv_start((uv_udp_t *)&this->socket_, &UVHelper::UVOnAllocCb,
                          &UVHelper::UVUdpRecvCb);
  PandaCheck(ret == 0) << " Ret:" << ret << " Error:" << uv_err_name(ret);
  this->bind_address_ = true;
}

bool UDPSocketImpl::Bind(const Panda::SocketAddress &address) {
  if (ThreadManagerImpl::Instance()->Current() != thread_) {
    return thread_->Invoke<bool>(PandaFromHere,
                                 [&]() { return this->Bind(address); });
  }
  PandaCheck(ThreadManagerImpl::Instance()->Current() == thread_);
  int ret =
      uv_udp_bind((uv_udp_t *)&this->socket_, GetIp4UVAddress(address), 0);
  PandaCheck(ret == 0 || ret == -48)
      << " Set bind address Ret:" << ret << " Error:" << uv_err_name(ret)
      << " ip:" << address.ToString();
  if (ret == -48) {
    return false;
  }
  UpdateLocalAddress();
  return true;
}

void UDPSocketImpl::SendTo(char *data, size_t len, Panda::SocketAddress &to) {
  PandaCheck(ThreadManagerImpl::Instance()->Current() == thread_);
  sendBuffer_ = uv_buf_init(data, len);
  UDPSendData *send = new UDPSendData();
  send->req.data = send;
  send->data = data;
  send->socket = this;
  send->len = len;
  uv_udp_send(&send->req, (uv_udp_t *)&this->socket_,
              (uv_buf_t *)&this->sendBuffer_, 1, GetIp4UVAddress(to),
              &UVHelper::UVOnUDPSendCb);
}

void UDPSocketImpl::Close() {
  PandaCheck(ThreadManagerImpl::Instance()->Current() == thread_);
  int ret = uv_udp_recv_stop((uv_udp_t *)&this->socket_);
  PandaCheck(ret == 0) << " Ret:" << ret << " Error:" << uv_err_name(ret);
  UVHandler::Close();
}

TCPSocketImpl::TCPSocketImpl(ThreadImpl *thread) {
  this->thread_ = thread;
  this->handler = &socket_;
  PandaCheck(ThreadManagerImpl::Instance()->Current() == thread_);
  int ret = uv_tcp_init_ex((uv_loop_t *)this->thread_->GetLoop(),
                           &this->socket_, AF_UNSPEC);
  PandaCheck(ret == 0) << " Ret:" << ret << " Error:" << uv_err_name(ret);
  uv_handle_set_data((uv_handle_t *)&this->socket_, this);
  this->isClosed = false;
}

TCPSocketImpl::~TCPSocketImpl() {
  // PandaCheck(ThreadManagerImpl::Instance()->Current() == thread_);
  this->thread_ = nullptr;
  if (this->buffer_) {
    free(this->buffer_);
    this->buffer_ = nullptr;
  }
}

bool TCPSocketImpl::Bind(const Panda::SocketAddress &address) {
  if (ThreadManagerImpl::Instance()->Current() != thread_) {
    return thread_->Invoke<bool>(PandaFromHere,
                                 [&]() { return this->Bind(address); });
  }
  int ret = uv_tcp_bind(&this->socket_, GetIp4UVAddress(address), 0);
  PandaCheck(ret == 0 || ret == -48)
      << " Set bind address Ret:" << ret << " Error:" << uv_err_name(ret)
      << " ip:" << address.ToString();
  if (ret == -48) {
    return false;
  }
  int addrLen = sizeof(sockaddr);
  struct sockaddr_in uvAddress;
  ret = uv_udp_getsockname((uv_udp_t *)&this->socket_, (sockaddr *)&uvAddress,
                           &addrLen);
  PandaCheck(ret == 0) << " Get bind address Ret:" << ret
                       << " Error:" << uv_err_name(ret);
  char ip[128] = {0};
  uv_ip_name((const sockaddr *)&uvAddress, ip, 128);
  this->local_ = Panda::SocketAddress(ip, ntohs(uvAddress.sin_port));
  this->bind_address_ = true;
  return true;
}

void TCPSocketImpl::Connect(const Panda::SocketAddress &remote) {
  if (ThreadManagerImpl::Instance()->Current() != thread_) {
    return thread_->Invoke(PandaFromHere,
                           [&]() { return this->Connect(remote); });
  }
  PandaCheck(ThreadManagerImpl::Instance()->Current() == thread_);
  this->connect_.data = this;
  this->remote_ = remote;
  int ret = uv_tcp_connect((uv_connect_t *)&connect_, &this->socket_,
                           GetIp4UVAddress(remote), &UVHelper::UVConnectCb);
  PandaCheck(ret == 0) << " Ret:" << ret << " Connect To:" << remote.ToString()
                       << " Error:" << uv_err_name(ret);
}

int TCPSocketImpl::Send(char *data, size_t len) {
  PandaCheck(ThreadManagerImpl::Instance()->Current() == thread_);
  if (isConnected == false || isClosing || isClosed) return -1;
  this->sendBuffer_ = uv_buf_init(data, len);

  TCPSendData *send = new TCPSendData();
  send->req.data = send;
  send->data = data;
  send->socket = this;
  send->len = len;
  int ret =
      uv_write(&send->req, (uv_stream_t *)&this->socket_,
               (uv_buf_t *)&this->sendBuffer_, 1, &UVHelper::UVOnTCPSendCb);
  PandaCheck(ret == 0) << " Ret:" << ret << " Write Error:" << uv_err_name(ret);
  return ret;
}

void TCPSocketImpl::Listen() {
  if (ThreadManagerImpl::Instance()->Current() != thread_) {
    return thread_->Invoke(PandaFromHere, [&]() { return this->Listen(); });
  }
  int ret = uv_listen((uv_stream_t *)&this->socket_, maxStreamNum,
                      &UVHelper::UVConnectionCb);
  PandaCheck(ret == 0) << " Ret:" << ret << " Error:" << uv_err_name(ret);
}

void TCPSocketImpl::Close() {
  PandaCheck(ThreadManagerImpl::Instance()->Current() == thread_);
  UVHandler::Close();
}

void TCPSocketImpl::OnConnected() {
  this->isConnected = true;
  int ret = uv_read_start((uv_stream_t *)&this->socket_, &UVHelper::UVOnAllocCb,
                          &UVHelper::UVReadCb);
  PandaCheck(ret == 0) << " Ret:" << ret << " Error:" << uv_err_name(ret);
  if (this->listener_) {
    this->listener_->OnConnected(this);
  }
}